package com.mall.util.distributed.raft.node;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

/**
 * 全局共同维护的结点信息
 *
 * 广播时间（broadcastTime） << 选举超时时间（electionTimeout） << 平均故障间隔时间（MTBF）
 */
@Data
@Accessors(chain = true)
public class Node {
    @Schema(description = "每个结点当前的任期号")
    private AtomicLong term = new AtomicLong(0);

    @Schema(description = "服务器最后一次知道任期")
    private AtomicLong currentTerm = new AtomicLong(0);

    @Schema(description = "当前结点角色，默认是跟随者")
    private volatile NodeRole nodeRole = NodeRole.FOLLOWER;

    @Schema(description = "当前结点的地址信息")
    private String selfAddress;

    @Schema(description = "其它结点的地址信息")
    private List<String> peerAddress;

    @Schema(description = "当前结点是否是Leader结点")
    private boolean isLeader = NodeRole.LEADER.equals(nodeRole);

    @Schema(description = "每个子结点需要保存leader结点信息")
    private String leaderId;

    @Schema(description = "当前结点投给的候选人 ID")
    private volatile String voteFor;

    @Schema(description = "上次选举超时时间")
    private volatile long preElectionTimeout = 0;

    @Schema(description = "选举时间间隔")
    private volatile long electionTime = 15 * 1000;

    @Schema(description = "上次心跳超时")
    private volatile long preHeartBeatTime;

    @Schema(description = "心跳随机超时 3s~5s")
    private volatile long heartBeatTick = 5 * 1000;

    /* ----------------------------------------------- 所有服务器都需要的 ----------------------------------------------- */
    @Schema(description = "服务器上已知的最大提交日志")
    private AtomicLong commitIndex = new AtomicLong(0);

    @Schema(description = "最后被应用到状态机的日志条目索引")
    private AtomicLong lastApplied = new AtomicLong(0);

    /* ----------------------------------------------- leader服务器需要的 ----------------------------------------------- */
    @Schema(description = "需要发送的下一个日志条目")
    private Map<String, Long> nextIndexs = new ConcurrentHashMap<>();

    @Schema(description = "需要复制的最高日志条目")
    private Map<String, Long> matchIndexs = new ConcurrentHashMap<>();

    private static final Node INSTANCE = new Node();
    public static Node getINSTANCE() {
        return INSTANCE;
    }

    /* 当选举出来leader之后，重置结点标记位 */
    public void resetMark() {
        setVoteFor(null);
    }

    /* 列举出非当前结点的所有自结点 */
    public List<String> getExcludeSelfPeerAddress() {
        return peerAddress.stream().filter(address -> {
            return !address.equals(selfAddress);
        }).collect(Collectors.toList());
    }
}
